/* 
Copyright (c) 2004,2005, 2006, 2007, 2008 University of Illinois 

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

3. The name of the author may not be used to endorse or promote products
   derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

  /*
  * SMF 8/3/07 We don't seem to be reading the toBeUninstalled info from the RDFService. This is not working.
  * http://daryl.learnhouston.com/archive/all/2005/3
  */
AccessibilityExtension.ns(function() { with (AccessibilityExtension.LIB) {
	
	/*
	W: Previous header
	S: Next header
	
	E: Previous navigation bar
	D: Next navigation bar
	
	Q: Previous link
	A: Next link
	*/
	
	var AE = AccessibilityExtension.ns;  
	var accessext_selectedTab = null;
	var inputFieldSpecialKeyPressed = false;
	
AccessibilityExtension.LIB.extend(AccessibilityExtension.LIB,
{
	 accessext_ToBeUninstalled: function() {
	    //Get various things needed to deal with RDF.
		var pref = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
		var prefBranch = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("");
	    var RDFService = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService(Components.interfaces.nsIRDFService);
	    var Container = Components.classes["@mozilla.org/rdf/container;1"].getService(Components.interfaces.nsIRDFContainer);
	    var extensionDS= Components.classes["@mozilla.org/extensions/manager;1"].getService(Components.interfaces.nsIExtensionManager).datasource;
	
	    //Get nodes and arc needed to figure out whether the given extension is slated to be uninstalled.
	    var root = RDFService.GetResource("urn:mozilla:item:root"); //8/3/07 SMF urn:mozilla:extension:root = old naming style
	    var nameArc = RDFService.GetResource("http://www.mozilla.org/2004/em-rdf#name");
	    var toBeUninstalledArc = RDFService.GetResource("http://www.mozilla.org/2004/em-rdf#toBeUninstalled");
	    //var toBeUninstalledArc = RDFService.GetResource("http://www.mozilla.org/2004/em-rdf#version");
	
	
	    Container.Init(extensionDS,root);
	
	    //Now iterate over the elements to find the toBeUninstalled value for the extension in question ("Extension Name," which you should change to match your extension name).
	    var found = false;
	    var elements = Container.GetElements();
	    while (elements.hasMoreElements()) {
	        var element = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
	        var name = "";
	        var toBeUninstalled = "";
	
	        var target = extensionDS.GetTarget(element, nameArc ,true);
	        if (target) {
	            name = target.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
	        }
	
	        target = extensionDS.GetTarget(element, toBeUninstalledArc ,true);
			//target never seems to get set
	        if (target) {
	            toBeUninstalled=target.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
	       }
	
	        //If we find the right value, set the found flag to true.
	        if (toBeUninstalled && (toBeUninstalled == "true") && (name == "Firefox Accessibility Extension")) {
	            found = true;
	        }
	    }
	},
	 
	accessext_nodeHanlder_LandmarkMain: function(node) {
			try {
				// This code would need to be filled out. We don't want to stop at every text node
				// for up/down arrow key presses
				var kFilterAccept = 1;
				var kFilterReject = 2;
				var kFilterSkip   = 3;
	
				if (node.nodeType == node.ELEMENT_NODE) {
					if (node instanceof Components.interfaces.nsIDOMHTMLElement) {
						if (node.hasAttribute('role')) {
							switch (node.getAttribute('role').toUpperCase()) {
								case 'MAIN':
									if (node.getAttribute('tabindex') == null || cleanSpaces(node.getAttribute('tabindex')) == '') node.setAttribute('tabindex', '-1'); //give object focus
									return kFilterAccept;
							}			
						}
					}
				}
				
				return kFilterSkip;
			} catch (ex) {
				accessext_consoleDump('accessext_nodeHanlder_LandmarkMain exception: ', ex);
			}
	},
	 
	accessext_nodeHanlder_LandmarkRoles: function(node) {
		try {
			// This code would need to be filled out. We don't want to stop at every text node
			// for up/down arrow key presses
			var kFilterAccept = 1;
			var kFilterReject = 2;
			var kFilterSkip   = 3;
	
			if (node.nodeType == node.ELEMENT_NODE) {
				if (node instanceof Components.interfaces.nsIDOMHTMLElement) {
					if (node.hasAttribute('role')) {
						switch (node.getAttribute('role').toUpperCase()) {
							case 'APPLICATION':
							case 'BANNER':
							case 'COMPLEMENTARY':
							case 'CONTENTINFO':
							case 'MAIN':
							case 'NAVIGATION':
							case 'SEARCH':
								if (node.getAttribute('tabindex') == null || cleanSpaces(node.getAttribute('tabindex')) == '') node.setAttribute('tabindex', '-1'); //give object focus
								return kFilterAccept;
						}			
					}
				}
			}
			
			return kFilterSkip;
		} catch (ex) {
			accessext_consoleDump('accessext_nodeHanlder_LandmarkRoles exception: ', ex);
		}
	},
	 
	accessext_nodeHanlder_Headers: function(node) {
		// This code would need to be filled out. We don't want to stop at every text node
		// for up/down arrow key presses
		var kFilterAccept = 1;
		var kFilterReject = 2;
		var kFilterSkip   = 3;
	
		if (node.nodeType == node.ELEMENT_NODE) {
			if (node instanceof Components.interfaces.nsIDOMHTMLElement) {
				switch (node.localName.toUpperCase()) {
					case 'H1':
					case 'H2':
					case 'H3':
					case 'H4':
					case 'H5':
					case 'H6':
						return kFilterAccept;
				}
			}
		}
		
		return kFilterSkip;
	},
	
	accessext_nodeHanlder_Navlist: function(node) {
		// This code would need to be filled out. We don't want to stop at every text node
		// for up/down arrow key presses
		var kFilterAccept = 1;
		var kFilterReject = 2;
		var kFilterSkip   = 3;
	
		if (node.nodeType == node.ELEMENT_NODE) {
			if (node instanceof Components.interfaces.nsIDOMHTMLElement) {
				switch (node.localName.toUpperCase()) {
					case 'UL':
					case 'OL':
					case 'DL':
						if (isNavList(node)) {
							return kFilterAccept;
						}
				}
			}
		}
		
		return kFilterSkip;
	},
	
	accessext_nodeHanlder_Links: function(node) {
		// This code would need to be filled out. We don't want to stop at every text node
		// for up/down arrow key presses
		var kFilterAccept = 1;
		var kFilterReject = 2;
		var kFilterSkip   = 3;
	
		if (node.nodeType == node.ELEMENT_NODE) {
			if (node instanceof Components.interfaces.nsIDOMHTMLElement) {
				switch (node.localName.toUpperCase()) {
					case 'A':
						return kFilterAccept;
				}
			}
		}
		
		return kFilterSkip;
	},
	
	isFocusInInputField: function(node) {
		if (node.form) {
			return true;
		}
		return false;
	},
	
	isFocusInTextField: function(node) {
		return (node.nodeName == 'TEXTAREA' || (node.form && node.type.toUpperCase() == 'TEXT'));
	},
	accessext_keyPressHandler: function(event) {
		/* Don't handle when alt or ctrl is pressed except in conjunction with a tab */
		if ((event.altKey || event.ctrlKey) && !((event.keyCode == 9 || event.keyCode == 77 || event.keyCode == 109) && event.altKey && event.ctrlKey)) {
			return true;
		}	
		
		/* Don't handle if off */
		var pref = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("");
		if (!pref.prefHasUserValue('accessext.keyboardnav.enabled') || (!pref.getBoolPref('accessext.keyboardnav.enabled'))) {
			return true;
		}
	
		var e = newKeyboardEnhancement("accessext.keyboard.enhancements.form.field.override.key","override")
		if (event.keyCode == event[e.getDOMString(e.getKey())] ) {
			inputFieldSpecialKeyPressed = true;//!inputFieldSpecialKeyPressed;
			return true;
		}
	
		/* Code from Aaron Leventhal's accessMozilla.js */
		var eventDoc = event.target.ownerDocument;
		var windowWatcher = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].
			getService(Components.interfaces.nsIWindowWatcher);
	
		var topLevelWin = windowWatcher.activeWindow;
		var eventWin = topLevelWin.document.commandDispatcher.focusedWindow;
		var selection = null;
		try {
			selection = eventWin.getSelection();
		} catch(e) {
		}
	
		/*
		* Only content documents that have a selection from this point forward
		* This does not include XUL documents, so they will exit early
		*/
		if (!selection) {
			return false;
		}
		var node = (selection.focusNode == null ?
			eventDoc.documentElement : selection.focusNode);
		if (isFocusInInputField(event.target) && !inputFieldSpecialKeyPressed) {
			return false;
		}
		
		var kNodeFilter = topLevelWin.NodeFilter.SHOW_ELEMENT;
	
		/* Mozilla has yet to implement createNodeIterator, so we kinda have to hack */
		var headerIterator = eventDoc.createTreeWalker(eventDoc.documentElement, kNodeFilter,accessext_nodeHanlder_Headers, true);
		var landMarkRoleIterator = eventDoc.createTreeWalker(eventDoc.documentElement, kNodeFilter,accessext_nodeHanlder_LandmarkRoles, true);
		var landMarkMainIterator = eventDoc.createTreeWalker(eventDoc.documentElement, kNodeFilter,accessext_nodeHanlder_LandmarkMain, true);
		var navListIterator = eventDoc.createTreeWalker(eventDoc.documentElement, kNodeFilter,accessext_nodeHanlder_Navlist, true);
		var linkIterator = eventDoc.createTreeWalker(eventDoc.documentElement, kNodeFilter,accessext_nodeHanlder_Links, true);
	
		var isNavigating = true;
		var found = false;
	
		e = new Array();
		e["prevHeader"] = newKeyboardEnhancement("accessext.keyboard.enhancements.feature.prev.header","key");
		e["nextHeader"] = newKeyboardEnhancement("accessext.keyboard.enhancements.feature.next.header","key");
		e["prevLandmark"] = newKeyboardEnhancement("accessext.keyboard.enhancements.feature.prev.landmark","key");
		e["nextLandmark"] = newKeyboardEnhancement("accessext.keyboard.enhancements.feature.next.landmark","key");
		e["prevLink"] = newKeyboardEnhancement("accessext.keyboard.enhancements.feature.prev.link","key");
		e["nextLink"] = newKeyboardEnhancement("accessext.keyboard.enhancements.feature.next.link","key");
		e["prevNavlist"] = newKeyboardEnhancement("accessext.keyboard.enhancements.feature.prev.navlist","key");
		e["nextNavlist"] = newKeyboardEnhancement("accessext.keyboard.enhancements.feature.next.navlist","key");	
	
		try {
			switch (event.keyCode) {
				case 109: //m 'Main' Landmark
				case 77: //m 'Main' Landmark
					if (event.altKey && event.ctrlKey && event.shiftKey) {
						landMarkMainIterator.currentNode = node;
						node = landMarkMainIterator.previousNode();		
						break;
					} else if (event.altKey && event.ctrlKey ) {
						landMarkMainIterator.currentNode = node;
						node = landMarkMainIterator.nextNode();
						break;
					}
				/* Headers */
				case event[e["prevHeader"].getDOMString(e["prevHeader"].getKey())]: 
					headerIterator.currentNode = node;
					node = headerIterator.previousNode();			
				break;
				case event[e["nextHeader"].getDOMString(e["nextHeader"].getKey())]: 
					headerIterator.currentNode = node;
					node = headerIterator.nextNode();
					/* Todo : refine this */
				break;
				/* Landmark Roles */
				case event[e["prevLandmark"].getDOMString(e["prevLandmark"].getKey())]: 
					landMarkRoleIterator.currentNode = node;
					node = landMarkRoleIterator.previousNode();			
				break;
				case event[e["nextLandmark"].getDOMString(e["nextLandmark"].getKey())]: 
					landMarkRoleIterator.currentNode = node;
					node = landMarkRoleIterator.nextNode();
				break;
				case 9: //tab
					if (event.altKey && event.ctrlKey && event.shiftKey) {
						landMarkRoleIterator.currentNode = node;
						node = landMarkRoleIterator.previousNode();			
					} else if (event.altKey && event.ctrlKey ) {
						landMarkRoleIterator.currentNode = node;
						node = landMarkRoleIterator.nextNode();
					} else isNavigating = false;
				break;
				/* Nav bars */
				case event[e["prevNavlist"].getDOMString(e["prevNavlist"].getKey())]: 
					navListIterator.currentNode = node;
					node = navListIterator.previousNode();	
				break;
				case event[e["nextNavlist"].getDOMString(e["nextNavlist"].getKey())]: 
					navListIterator.currentNode = node;
					node = navListIterator.nextNode();
				break;
				/* Links */
				case event[e["prevLink"].getDOMString(e["prevLink"].getKey())]:
					linkIterator.currentNode = node;
					node = linkIterator.previousNode();				
				break;
				case event[e["nextLink"].getDOMString(e["nextLink"].getKey())]:
					linkIterator.currentNode = node;
					node = linkIterator.nextNode();
				break;
				default:
					isNavigating = false;
			}
		} catch (e) {
			accessext_consoleDump('Exception: ' + e.toString());
			isNavigating = false;
		}
		
		if (node == null) {
			return false;
		} 
		try {
			if (isNavigating) {
				if (node.focus) {//Fix for MOZ1.7
					node.focus();
				}
				selection.collapse(node, 0);
				selection.removeAllRanges();
				var range = eventDoc.createRange();
				range.selectNodeContents(node);
				selection.addRange(range);
				//Do everything possible to keep the event from doing anything else
				
				//event.preventCapture(); deprecated
				event.stopPropagation();  //event.preventBubble(); SMF 3/29/08 removed in FF3 - use stopPropagation() instead
				event.preventDefault();
				if (isFocusInTextField(event.target)/* && event.target.value*/) {
					event.target.blur();
					//event.target.value = event.target.value.substr(0,event.target.value.length - 1);
					inputFieldSpecialKeyPressed = false;
				}
				
				accessext_scrollToNodeCentered(node);
				accessext_keyboardFocusNode(node,window);
			}
		} catch (e) {
			accessext_consoleDump('Exception in selection: ' + e);
			return false;
		}
		return true;
	},
	
	accessext_isEventDocumentTarget: function(event) {
		return event.target && event.target.hasAttribute && event.target.hasAttribute("id") && event.target.getAttribute("id").toLowerCase() == "content";
	},
	
	accessext_recursivelyAddEventsToDocument: function(doc) {
	// Try to remove the event listener
		try {
			doc.removeEventListener("keydown", accessext_keyPressHandler, false);
		} catch(exception) {
			accessext_consoleDump('error removing keydown event handler from contentDocument');
			// Do nothing
		}
		try {
			doc.addEventListener("keydown", accessext_keyPressHandler, false);
		} catch (exception) {
			accessext_consoleDump('error adding keydown event handler from contentDocument');
		}
		try {
			var fr = doc.getElementsByTagName('frame');
			if (fr.length > 0) {
				for (var i = 0; i < fr.length; i++) {
					if (fr[i].contentDocument && fr[i].contentDocument.documentElement) {
						accessext_recursivelyAddEventsToDocument(fr[i].contentDocument.documentElement);
					}
				}
			}
		} catch (ex) {
			alert("Exception in recursion: " + ex);
		}
	},
	
	accessext_addEventsToTabDocuments: function(doc) {
		try {
			//VERY greedy algorithm
			var tabContainer = document.getElementById('content');
			if (tabContainer) {
				if (tabContainer.browsers.length > 0) {
					for (var i = 0; i < tabContainer.browsers.length; i++) {
						if (tabContainer.browsers[i].contentDocument && tabContainer.browsers[i].contentDocument.documentElement) {
							accessext_recursivelyAddEventsToDocument(tabContainer.browsers[i].contentDocument.documentElement);
						}
					}
				}
			}
		} catch (exception) {
			alert("Exception: " + exception);
		}
	},
	
	dumpEvent: function(event) {
		var dump = "";
		for (var a in event) {
			if ((''+a).toLowerCase() != 'canfindagain' && event[a]) {
				dump = dump + a + ":" + event[a] + "\n";
			}
		}
		accessext_consoleDump(dump);
	},
	
	/**
	 * Action needs to be taken when particular options are off. accessext_formatPageReverse() is called when a
	 * new window is loaded to handle the options that their default state is true.
	 * @param {aEvent}  aEvent contains the document that triggered "onload" event
	 * @return nothing
	 * @author Sandy Foltz
	*/
	
	accessext_formatPageReverse: function(aEvent) {
	    var doc = aEvent.target.contentDocument; // doc is document that triggered "onload" event
	    //read the current accessext settings and apply them
		if (accessext_getActivePreferenceMode() == 'global') {
			var docArray = new Array();
			docArray[0] = doc;
			
			//saved settings not current settings I think	
			var preferencesService = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("");	
			
			//style
			//accessext_consoleDump('accessext.styles.author.css.enabled =' + preferencesService.getBoolPref("accessext.styles.author.css.enabled"));
			if (preferencesService.getBoolPref("accessext.styles.author.css.enabled") == false) {
				accessext_handle_styles_css_enabled(false,docArray);
			}		
			if (preferencesService.getBoolPref("accessext.styles.css.tags.enabled") == false) { //accessext.styles.disable.css.tags
				accessext_handle_styles_css_tags_enabled(false,docArray);
			}		
	    	if (preferencesService.getBoolPref("accessext.styles.css.tables.enabled") == false) { //aaccessext.styles.disable.css.tables
				accessext_handle_styles_tables_enabled(false,docArray);
			}
		}	
	},
	
	
	/*
	For frames, the event target document always appears to be the tabbrowser element
	*/
	TEST: function(event) {
		alert('here')
	},
	
	accessext_pageLoad: function(event) {
		//determine if the doc's URL is != the current URL - if so, its probably a nested frame, and we can discard it
		//This will be messy for nested frames, but those probably should never be used anyhow ;)
	
		var eventTarget = event.target;
		var preferencesService = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("");
		var element = null;
		var val = false;
			
	   if (accessext_isEventDocumentTarget(event)) {
			//Fix for menu positioning
			if (!accessext_is_firefox()) {
				accessext_menuFix();
			}
	
			var contentDocument = eventTarget.contentDocument;
			accessext_activateDocument(contentDocument);
			accessext_updateMenus();
			accessext_handle_showMenus();
			
	 		var navMenu = document.getElementById('accessext-mainmenu-navigation');
			var navSubMenu = document.getElementById('accessext-menu-navigation');
			
			// It doesn't seem as though this preference is used anywhere 
			// ... but comment out the below line to enable its functionality
			hideNavMain = true;
		
			if (navMenu && navSubMenu) {
				navMenu.hidden = hideNavMain;
				navSubMenu.hidden = !hideNavMain;
			}
	
			/*accessext_userStylesheet = preferencesService.getCharPref("accessext.styles.userStylesheet.text");*/
			accessext_addEventsToTabDocuments(contentDocument);
			
			accessext_enablePersistentOptions(contentDocument);
			accessext_formatPageReverse(event);
	
			//Reload shortcuts, in case they changed ***purposly last accessextKeybinding.loadBinding() fails w/o logging errors to console ***
			var bindings = newAccessextKeybindingManager();
			bindings.configureKeyboardShortcuts();		
	
		} else {
		//	accessext_consoleDump('Page load, but was not content document.');
		}
	},
	
	/*
	TODO: This is not very tab friendly
	*/
	accessext_pageUnload: function(event) {
		var l = AE.accessext_openWindows.length;
		for (var i=0; i<l; i++) {
			var temp = AE.accessext_openWindows[i];
			temp.close();
		}
	
		AE.accessext_openWindows = Array();
	    try {
			if (accessext_ToBeUninstalled() == true) { //SMF 8/2/07 remove preferences
				var pref = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
				pref.deleteBranch ("");
			}
		} catch (ex) {
			accessext_consoleDump('accessext_pageUnload failed.', ex);
		}	
	},
	
	accessext_windowFocus: function(event) {
		//Be advised : this is called twice on each window focus event. Need to get around this, though it is a low priority
		var hasMenu = document.getElementById("radio-prefStandard-icita07");
		if (hasMenu != null) {
			accessext_updateMenus();		
		} 
	},
	
	accessext_tabSelect: function(event) {
	//POTENTIAL SEAMONKEY BUG
		var selectedTab     = getBrowser().mTabBox.selectedIndex;
		var currentDocument = getBrowser().browsers[selectedTab].contentWindow.document;
	
		// If the selected tab is different
		if(selectedTab != accessext_selectedTab) {
			var preferencesService = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("");
			accessext_selectedTab = selectedTab;
			accessext_activateDocument(currentDocument);
			accessext_updateMenus();
		}
	}
});
}});		